#define TRUE  1
#define FALSE 0
#define bool uint8_t

#define _USE_WRITE   1

#define _SPI_USE_DMA 0

#include <stdio.h>
#include <string.h>

#include "fatfs_sd.h"

static DMA_InitTypeDef DMA_I2S_InitStructure;

uint16_t Timer1, Timer2;										/* 1ms Timer Counter */

static volatile uint8_t Stat = STA_NOINIT;	/* Disk Status */

static uint8_t CardType;                    /* Type 0:MMC, 1:SDC, 2:Block addressing */
static uint8_t PowerFlag = 0;								/* Power flag */

void SPI3_Config(void);
void SPI3_DMA_Config(void);

static void SELECT(void);
static void DESELECT(void);
static void SD_PowerOn(void);
static void SD_PowerOff(void);
static uint8_t SD_ReadyWait(void);
static uint8_t SD_CheckPower(void);
static uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg);

/***************************************
 * SPI functions
 **************************************/

/* SPI transmit a byte */
static int SPI_TxByte(uint8_t data)
{

#if _SPI_USE_DMA

    uint8_t sendbyte = data, rcv;
    // DMA_InitTypeDef DMA_I2S_InitStructure;

    /* Disable DMA SPI RX Stream */
    DMA_Cmd(SPI3_RX_DMA_STREAM, DISABLE);
    while (DMA_GetCmdStatus(SPI3_RX_DMA_STREAM) != DISABLE);

    DMA_I2S_InitStructure.DMA_BufferSize = 1;
    DMA_I2S_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
	
	DMA_I2S_InitStructure.DMA_Memory0BaseAddr = (uint32_t) & (rcv);
    DMA_I2S_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
	DMA_I2S_InitStructure.DMA_Channel = SPI3_RX_DMA_CHANNEL;
    DMA_Init(SPI3_RX_DMA_STREAM, &DMA_I2S_InitStructure);

    DMA_DeInit(SPI3_TX_DMA_STREAM);
    while (DMA_GetCmdStatus(SPI3_TX_DMA_STREAM) != DISABLE);

    DMA_I2S_InitStructure.DMA_Memory0BaseAddr = (uint32_t) & (sendbyte);
    DMA_I2S_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral ;
	DMA_I2S_InitStructure.DMA_Channel = SPI3_TX_DMA_CHANNEL;
    DMA_Init(SPI3_TX_DMA_STREAM, &DMA_I2S_InitStructure);

    DMA_ClearFlag(SPI3_RX_DMA_STREAM, SPI3_RX_DMA_FLAG_TCIF);
    DMA_ClearFlag(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF);

    DMA_Cmd(SPI3_RX_DMA_STREAM, ENABLE);
    DMA_Cmd(SPI3_TX_DMA_STREAM, ENABLE);

    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, ENABLE);

    while (!DMA_GetFlagStatus(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF));
    while (!DMA_GetFlagStatus(SPI3_RX_DMA_STREAM, SPI3_RX_DMA_FLAG_TCIF));

    DMA_Cmd(SPI3_RX_DMA_STREAM, DISABLE);
    DMA_Cmd(SPI3_TX_DMA_STREAM, DISABLE);

    DMA_ClearFlag(SPI3_RX_DMA_STREAM, SPI3_RX_DMA_FLAG_TCIF);
    DMA_ClearFlag(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF);
	
    return rcv;

#else

    while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI3, data);
    return 0;

#endif

}

/* SPI transmit buffer */
static void SPI_TxBuffer(uint8_t *buffer, uint16_t len)
{
#if _SPI_USE_DMA

    // DMA_InitTypeDef  DMA_I2S_InitStructure;
    DMA_DeInit(SPI3_TX_DMA_STREAM); //tx
    while (DMA_GetCmdStatus(SPI3_TX_DMA_STREAM) != DISABLE);

    DMA_I2S_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buffer;
    DMA_I2S_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral ;
    DMA_I2S_InitStructure.DMA_BufferSize = len;
    DMA_I2S_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_I2S_InitStructure.DMA_Channel = SPI3_TX_DMA_CHANNEL;	
	DMA_Init(SPI3_TX_DMA_STREAM, &DMA_I2S_InitStructure);

    DMA_ClearFlag(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF);

    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);

    DMA_Cmd(SPI3_TX_DMA_STREAM, ENABLE);

    while(!DMA_GetFlagStatus(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF));
	
    DMA_Cmd(SPI3_TX_DMA_STREAM, DISABLE);
    DMA_ClearFlag(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF);

#else
    for(int i = 0; i < len; i++)
        SPI_TxByte(*buffer++);

#endif
}

/* SPI receive a byte */
static uint8_t SPI_RxByte(void)
{
#if _SPI_USE_DMA

    return SPI_TxByte(0xff);

#else
    uint16_t dummy, data;
    dummy = 0x00FF;

    while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
    SPI_I2S_SendData(SPI3, dummy);
    while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET);
    data =  SPI_I2S_ReceiveData(SPI3);
	return (uint8_t)data;
#endif

}

/* SPI receive a byte via pointer */
static void SPI_RxBytePtr(uint8_t *buff)
{
    *buff = SPI_RxByte();
}

/* receive data block */
static bool SD_RxDataBlock(uint8_t *buff, uint16_t len)
{

    uint8_t token;

    /* timeout 200ms */
    Timer1 = 200;

    /* loop until receive a response or timeout */
    do
    {
        token = SPI_RxByte();
    }
    while((token == 0xFF) && Timer1);

    /* invalid response */
    if(token != 0xFE) return FALSE;

#if _SPI_USE_DMA

    uint8_t temp = 0xff;
	
    // DMA_InitTypeDef DMA_I2S_InitStructure;

    /* Disable DMA SPI RX Stream */
    DMA_Cmd(SPI3_RX_DMA_STREAM, DISABLE);
    while (DMA_GetCmdStatus(SPI3_RX_DMA_STREAM) != DISABLE);

    DMA_I2S_InitStructure.DMA_BufferSize = len;
    
    DMA_I2S_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buff;
    DMA_I2S_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;
	DMA_I2S_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_I2S_InitStructure.DMA_Channel = SPI3_RX_DMA_CHANNEL;	
    DMA_Init(SPI3_RX_DMA_STREAM, &DMA_I2S_InitStructure);

    // DMA_DeInit(SPI3_TX_DMA_STREAM);
	DMA_Cmd(SPI3_TX_DMA_STREAM, DISABLE);
    while (DMA_GetCmdStatus(SPI3_TX_DMA_STREAM) != DISABLE);

    DMA_I2S_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&temp;
    DMA_I2S_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral ;
    DMA_I2S_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    DMA_I2S_InitStructure.DMA_Channel = SPI3_TX_DMA_CHANNEL;
	
    DMA_Init(SPI3_TX_DMA_STREAM, &DMA_I2S_InitStructure);

    DMA_ClearFlag(SPI3_RX_DMA_STREAM, SPI3_RX_DMA_FLAG_TCIF);
    DMA_ClearFlag(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF);

    DMA_Cmd(SPI3_RX_DMA_STREAM, ENABLE);
    DMA_Cmd(SPI3_TX_DMA_STREAM, ENABLE);

    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
    SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, ENABLE);

    while (!DMA_GetFlagStatus(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF));
    while (!DMA_GetFlagStatus(SPI3_RX_DMA_STREAM, SPI3_RX_DMA_FLAG_TCIF));

    DMA_Cmd(SPI3_RX_DMA_STREAM, DISABLE);
    DMA_Cmd(SPI3_TX_DMA_STREAM, DISABLE);

    DMA_ClearFlag(SPI3_RX_DMA_STREAM, SPI3_RX_DMA_FLAG_TCIF);
    DMA_ClearFlag(SPI3_TX_DMA_STREAM, SPI3_TX_DMA_FLAG_TCIF);

#else
    /* receive data */
    do
    {
        SPI_RxBytePtr(buff++);
    }
    while(len--);

#endif

    /* discard CRC */
    SPI_RxByte();
    SPI_RxByte();

    return TRUE;
}

/* transmit data block */
#if _USE_WRITE == 1
static bool SD_TxDataBlock(const uint8_t *buff, uint8_t token)
{
    uint8_t resp;
    uint8_t i = 0;

    /* wait SD ready */
    if (SD_ReadyWait() != 0xFF) return FALSE;

    /* transmit token */
    SPI_TxByte(token);

    /* if it's not STOP token, transmit data */
    if (token != 0xFD)
    {
        SPI_TxBuffer((uint8_t *)buff, 512);

        /* discard CRC */
        SPI_RxByte();
        SPI_RxByte();

        /* receive response */
        while (i <= 64)
        {
            resp = SPI_RxByte();

            /* transmit 0x05 accepted */
            if ((resp & 0x1F) == 0x05) break;
            i++;
        }

        /* recv buffer clear */
        while (SPI_RxByte() == 0);
    }

    /* transmit 0x05 accepted */
    if ((resp & 0x1F) == 0x05) return TRUE;

    return FALSE;
}
#endif /* _USE_WRITE */

/***************************************
 * user_diskio.c functions
 **************************************/

/* initialize SD */
uint8_t SD_disk_initialize(uint8_t drv)
{
    uint8_t n, type, ocr[4];

    /* single drive, drv should be 0 */
    if(drv) return STA_NOINIT;

    /* no disk */
    if(Stat & STA_NODISK) return Stat;

    /* power on */
    SD_PowerOn();

    /* slave select */
    SELECT();

    /* check disk type */
    type = 0;

    /* send GO_IDLE_STATE command */
    if (SD_SendCmd(CMD0, 0) == 1)
    {
        /* timeout 1 sec */
        Timer1 = 100;

        /* SDC V2+ accept CMD8 command, http://elm-chan.org/docs/mmc/mmc_e.html */
        if (SD_SendCmd(CMD8, 0x1AA) == 1)
        {
            /* operation condition register */
            for (n = 0; n < 4; n++)
            {
                ocr[n] = SPI_RxByte();
            }

            /* voltage range 2.7-3.6V */
            if (ocr[2] == 0x01 && ocr[3] == 0xAA)
            {
                /* ACMD41 with HCS bit */
                do
                {
                    if (SD_SendCmd(CMD55, 0) <= 1 && SD_SendCmd(CMD41, 1UL << 30) == 0) break;
                }
                while (Timer1);

                /* READ_OCR */
                if (Timer1 && SD_SendCmd(CMD58, 0) == 0)
                {
                    /* Check CCS bit */
                    for (n = 0; n < 4; n++)
                    {
                        ocr[n] = SPI_RxByte();
                    }

                    /* SDv2 (HC or SC) */
                    type = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
                }
            }
        }
        else
        {
            /* SDC V1 or MMC */
            type = (SD_SendCmd(CMD55, 0) <= 1 && SD_SendCmd(CMD41, 0) <= 1) ? CT_SD1 : CT_MMC;
            do
            {
                if (type == CT_SD1)
                {
                    if (SD_SendCmd(CMD55, 0) <= 1 && SD_SendCmd(CMD41, 0) == 0) break; /* ACMD41 */
                }
                else
                {
                    if (SD_SendCmd(CMD1, 0) == 0) break; /* CMD1 */
                }
            }
            while (Timer1);
			
            /* SET_BLOCKLEN */
            if (!Timer1 || SD_SendCmd(CMD16, 512) != 0) type = 0;
        }
    }

    CardType = type;

	if( type & CT_MMC) printf("CardType  CT_MMC\r\n");
	if( type & CT_SD1) printf("CardType  CT_SD1\r\n");
	if( type & CT_SD2) printf("CardType  CT_SD2\r\n");
	if( type & CT_SDC) printf("CardType  CT_SDC\r\n");
	if( type & CT_BLOCK) printf("CardType  CT_BLOCK\r\n");
	
	
    /* Idle */
    DESELECT();
    SPI_RxByte();

    /* Clear STA_NOINIT */
    if (type)
    {
        Stat &= ~STA_NOINIT;
    }
    else
    {
        /* Initialization failed */
        SD_PowerOff();
    }

    return Stat;
}

/* return disk status */
uint8_t SD_disk_status(uint8_t drv)
{
    if (drv) return STA_NOINIT;
    return Stat;
}

/* read sector */
uint8_t SD_disk_read(uint8_t pdrv, uint8_t *buff, uint32_t sector, uint16_t count)
{
    /* pdrv should be 0 */
    if (pdrv || !count) return RES_PARERR;

    /* no disk */
    if (Stat & STA_NOINIT) return RES_NOTRDY;

    /* convert to byte address */
    if (!(CardType & CT_SD2)) sector *= 512;

    SELECT();

    if (count == 1)
    {
        /* READ_SINGLE_BLOCK */
        if ((SD_SendCmd(CMD17, sector) == 0) && SD_RxDataBlock(buff, 512)) count = 0;
    }
    else
    {
        /* READ_MULTIPLE_BLOCK */
        if (SD_SendCmd(CMD18, sector) == 0)
        {
            do
            {
                if (!SD_RxDataBlock(buff, 512)) break;
                buff += 512;
            }
            while (--count);

            /* STOP_TRANSMISSION */
            SD_SendCmd(CMD12, 0);
        }
    }

    /* Idle */
    DESELECT();
    SPI_RxByte();

    return count ? RES_ERROR : RES_OK;
}

/* write sector */

#if _USE_WRITE == 1
uint8_t SD_disk_write(uint8_t pdrv, const uint8_t *buff, uint32_t sector, uint16_t count)
{
    /* pdrv should be 0 */
    if (pdrv || !count) return RES_PARERR;

    /* no disk */
    if (Stat & STA_NOINIT) return RES_NOTRDY;

    /* write protection */
    if (Stat & STA_PROTECT) return RES_WRPRT;

    /* convert to byte address */
    if (!(CardType & CT_SD2)) sector *= 512;

    SELECT();

    if (count == 1)
    {
        /* WRITE_BLOCK */
        if ((SD_SendCmd(CMD24, sector) == 0) && SD_TxDataBlock(buff, 0xFE))
            count = 0;
    }
    else
    {
        /* WRITE_MULTIPLE_BLOCK */
        if (CardType & CT_SD1)
        {
            SD_SendCmd(CMD55, 0);
            SD_SendCmd(CMD23, count); /* ACMD23 */
        }

        if (SD_SendCmd(CMD25, sector) == 0)
        {
            do
            {
                if(!SD_TxDataBlock(buff, 0xFC)) break;
                buff += 512;
            }
            while (--count);

            /* STOP_TRAN token */
            if(!SD_TxDataBlock(0, 0xFD))
            {
                count = 1;
            }
        }
    }

    /* Idle */
    DESELECT();
    SPI_RxByte();

    return count ? RES_ERROR : RES_OK;
}
#endif /* _USE_WRITE */

/* ioctl */
uint8_t SD_disk_ioctl(uint8_t drv, uint8_t ctrl, void *buff)
{
    uint8_t res;
    uint8_t n, csd[16], *ptr = buff;
    uint16_t csize;

    /* pdrv should be 0 */
    if (drv) return RES_PARERR;
    res = RES_ERROR;

    if (ctrl == CTRL_POWER)
    {
        switch (*ptr)
        {
        case 0:
            SD_PowerOff();		/* Power Off */
            res = RES_OK;
            break;
        case 1:
            SD_PowerOn();		/* Power On */
            res = RES_OK;
            break;
        case 2:
            *(ptr + 1) = SD_CheckPower();
            res = RES_OK;		/* Power Check */
            break;
        default:
            res = RES_PARERR;
        }
    }
    else
    {
        /* no disk */
        if (Stat & STA_NOINIT) return RES_NOTRDY;

        SELECT();

        switch (ctrl)
        {
        case GET_SECTOR_COUNT:
            /* SEND_CSD */
            if ((SD_SendCmd(CMD9, 0) == 0) && SD_RxDataBlock(csd, 16))
            {
                if ((csd[0] >> 6) == 1)
                {
                    /* SDC V2 */
                    csize = csd[9] + ((uint16_t) csd[8] << 8) + 1;
                    *(uint32_t *) buff = (uint32_t) csize << 10;
                }
                else
                {
                    /* MMC or SDC V1 */
                    n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                    csize = (csd[8] >> 6) + ((uint16_t) csd[7] << 2) + ((uint16_t) (csd[6] & 3) << 10) + 1;
                    *(uint32_t *) buff = (uint32_t) csize << (n - 9);
                }
                res = RES_OK;
            }
            break;
        case GET_SECTOR_SIZE:
            *(uint16_t *) buff = 512;
            res = RES_OK;
            break;
        case CTRL_SYNC:
            if (SD_ReadyWait() == 0xFF) res = RES_OK;
            break;
        case MMC_GET_CSD:
            /* SEND_CSD */
            if (SD_SendCmd(CMD9, 0) == 0 && SD_RxDataBlock(ptr, 16)) res = RES_OK;
            break;
        case MMC_GET_CID:
            /* SEND_CID */
            if (SD_SendCmd(CMD10, 0) == 0 && SD_RxDataBlock(ptr, 16)) res = RES_OK;
            break;
        case MMC_GET_OCR:
            /* READ_OCR */
            if (SD_SendCmd(CMD58, 0) == 0)
            {
                for (n = 0; n < 4; n++)
                {
                    *ptr++ = SPI_RxByte();
                }
                res = RES_OK;
            }
        default:
            res = RES_PARERR;
        }

        DESELECT();
        SPI_RxByte();
    }

    return res;
}

/***************************************
 * SD functions
 **************************************/

/* transmit command */
static uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg)
{
    uint8_t crc, res;

    /* wait SD ready */
    if (SD_ReadyWait() != 0xFF) return 0xFF;

    /* transmit command */
    SPI_TxByte(cmd); 					/* Command */
    SPI_TxByte((uint8_t)(arg >> 24)); 	/* Argument[31..24] */
    SPI_TxByte((uint8_t)(arg >> 16)); 	/* Argument[23..16] */
    SPI_TxByte((uint8_t)(arg >> 8)); 	/* Argument[15..8] */
    SPI_TxByte((uint8_t)arg); 			/* Argument[7..0] */

    /* prepare CRC */
    if(cmd == CMD0) crc = 0x95;	/* CRC for CMD0(0) */
    else if(cmd == CMD8) crc = 0x87;	/* CRC for CMD8(0x1AA) */
    else crc = 1;

    /* transmit CRC */
    SPI_TxByte(crc);

    /* Skip a stuff byte when STOP_TRANSMISSION */
    if (cmd == CMD12) SPI_RxByte();

    /* receive response */
    uint8_t n = 10;
    do
    {
        res = SPI_RxByte();
    }
    while ((res & 0x80) && --n);

    return res;
}

/* slave select */
static void SELECT(void)
{
    GPIO_ResetBits(TF_CS_PORT,  TF_CS);
}

/* slave deselect */
static void DESELECT(void)
{
    GPIO_SetBits(TF_CS_PORT,  TF_CS);
}

/* power on */
static void SD_PowerOn(void)
{
    uint8_t args[6];
    uint32_t cnt = 0x1FFF;

    /* transmit bytes to wake up */
    DESELECT();
    // printf("[power on] wake up ");
    for(int i = 0; i < 10; i++)
    {
        SPI_TxByte(0xFF);
    }
    // printf("ok\r\n");
    /* slave select */
    SELECT();

    /* make idle state */
    args[0] = CMD0;		/* CMD0:GO_IDLE_STATE */
    args[1] = 0;
    args[2] = 0;
    args[3] = 0;
    args[4] = 0;
    args[5] = 0x95;		/* CRC */

    SPI_TxBuffer(args, sizeof(args));
    // printf("[power on] make idle state ok\r\n");
    /* wait response */
    while ((SPI_RxByte() != 0x01) && cnt)
    {
        cnt--;
        // printf("[power on] wait for response... %x \r\n", cnt);
    }

    DESELECT();
    // SPI_TxByte(0XFF);

    PowerFlag = 1;
}

/* power off */
static void SD_PowerOff(void)
{
    PowerFlag = 0;
}

/* check power flag */
static uint8_t SD_CheckPower(void)
{
    return PowerFlag;
}

/* wait SD ready */
static uint8_t SD_ReadyWait(void)
{
    uint8_t res;

    /* timeout 500ms */
    Timer2 = 500;

    /* if SD goes ready, receives 0xFF */
    do
    {
        res = SPI_RxByte();
    }
    while ((res != 0xFF) && Timer2);

    return res;
}

/**
  * @brief  Configure the SPI3 Channels. DMA.
  * @param  None
  * @retval None
	* @PINs   PB3->SCK  PB4->MISO PB5->MOSI PA15->CS
  */
void SPI3_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef  SPI_InitStructure;

	/* Peripheral Clock Enable -------------------------------------------------*/

	/* Enable GPIO clocks */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

	/* Configure TF_CS */
	GPIO_InitStructure.GPIO_Pin = TF_CS | FLASH_CS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(TF_CS_PORT, &GPIO_InitStructure);

	GPIO_SetBits(TF_CS_PORT,  TF_CS);
	GPIO_SetBits(TF_CS_PORT,  FLASH_CS);

	/* Connect SPI pins to GPIO_AF_SPI3 */
	GPIO_PinAFConfig(TF_PORT, TF_SCK_SOURCE, GPIO_AF_SPI3);
	GPIO_PinAFConfig(TF_PORT, TF_MISO_SOURCE, GPIO_AF_SPI3);
	GPIO_PinAFConfig(TF_PORT, TF_MOSI_SOURCE, GPIO_AF_SPI3);
	// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE) ;

	/* SPI GPIO Configuration --------------------------------------------------*/
	// GPIO_DeInit(TF_PORT);
	/* Configure LCD_CS LCD_SCK LCD_SDA */
	GPIO_InitStructure.GPIO_Pin = TF_SCK | TF_MISO | TF_MOSI;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
	GPIO_Init(TF_PORT, &GPIO_InitStructure);
	
	/* Enable the SPI clock */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);

	/* SPI configuration -------------------------------------------------------*/
	SPI_I2S_DeInit(SPI3);
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;

	SPI_Init(SPI3, &SPI_InitStructure);
	// SPI_SSOutputCmd(SPI3, ENABLE);
	SPI_Cmd(SPI3, ENABLE);

}

void SPI3_DMA_Config(void)
{
	// DMA_InitTypeDef DMA_I2S_InitStructure;
	
	/* Enable DMA clock */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

	/* DMA configuration -------------------------------------------------------*/
	/* Deinitialize DMA Streams */
	DMA_DeInit(SPI3_TX_DMA_STREAM);
	DMA_DeInit(SPI3_RX_DMA_STREAM);

	/* Configure DMA Initialization Structure */
	DMA_I2S_InitStructure.DMA_BufferSize = (uint32_t)0xfffa;
	DMA_I2S_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable ;
	DMA_I2S_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ;
	DMA_I2S_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single ;
	DMA_I2S_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_I2S_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_I2S_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_I2S_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) (&(SPI3->DR)) ;
	DMA_I2S_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	DMA_I2S_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_I2S_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_I2S_InitStructure.DMA_Priority = DMA_Priority_Medium;
	/* Configure TX DMA */
	DMA_I2S_InitStructure.DMA_Channel = SPI3_TX_DMA_CHANNEL ;
	DMA_I2S_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral ;
	DMA_I2S_InitStructure.DMA_Memory0BaseAddr = (uint32_t)0;
	DMA_Init(SPI3_TX_DMA_STREAM, &DMA_I2S_InitStructure);
	/* Configure RX DMA */
	DMA_I2S_InitStructure.DMA_Channel = SPI3_RX_DMA_CHANNEL ;
	DMA_I2S_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;
	DMA_I2S_InitStructure.DMA_Memory0BaseAddr = (uint32_t)0;
	DMA_Init(SPI3_RX_DMA_STREAM, &DMA_I2S_InitStructure);

	/* Enable DMA SPI TX Stream */
	// DMA_Cmd(SPI3_TX_DMA_STREAM, ENABLE);

	/* Enable DMA SPI RX Stream */
	// DMA_Cmd(SPI3_RX_DMA_STREAM, ENABLE);

	/* Enable SPI DMA TX Requsts */
	// SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);

	/* Enable SPI DMA RX Requsts */
	// SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, ENABLE);

}

void SdCard_init(void)
	
{
	SPI3_Config();
	
#if _SPI_USE_DMA
	
	SPI3_DMA_Config();
	
#endif

}


